// © 2009 Microsoft Corporation.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using System.Timers;
using Microsoft.InformationSecurity.ISRM.TaskManagment.Classes;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Reflection;

namespace Microsoft.InformationSecurity.ISRM.TaskManagment.NotificationsService
{
    internal static class ServiceManager
    {
        private static Timer serviceTimer;
        public static void InitService() 
        {
            Logger.Write("Starting the service");
            Logger.Write("Initializing service timer");
            serviceTimer = new Timer();
            serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
            serviceTimer.Interval = 60 * 1000;
            serviceTimer.Enabled = true;
        }

        static void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            serviceTimer.Stop();
            try
            {
                ProcessNotifications();
            }
            catch (Exception  ex) 
            {
                Logger.Write(ex.ToString());
            }
            serviceTimer.Start();
        }

        public static void TerminateService() 
        {
            Logger.Write("Stopping the service timer");
            serviceTimer.Stop();
            serviceTimer.Enabled = false;
            serviceTimer = null;
            Logger.Write("Terminating the service");
        }

        private static void ProcessNotifications() 
        {

            Logger.Write("Checking the current hours and minutes with the configuration hours and minutes to buid notifications");
            DateTime currentDateTime = DateTime.Now;
            if (currentDateTime.TimeOfDay.Hours == ServiceConfiguration.NotificationsBuildTime.Hours && currentDateTime.TimeOfDay.Minutes == ServiceConfiguration.NotificationsBuildTime.Minutes)
            {
                string strNotificationIDs = "";
                Logger.Write("Retrieving all notifications from database");
                List<Classes.Notification> lstNotifications = Notifications.GetAssignedNotifications();
                foreach (Classes.Notification n in lstNotifications)
                {
                    if (n.IsRecurring == true)
                    {
                        switch (n.FrequencyType)
                        {
                            case FrequencyType.Daily:
                                if (currentDateTime.Subtract(n.LastProcessedDate).Days >= n.Frequency)
                                {
                                    n.IsDueForProcessing = true;
                                }
                                break;
                            case FrequencyType.Weekly:
                                if ((currentDateTime.Subtract(n.LastProcessedDate).Days / 7) >= n.Frequency && ((n.DayOfWeek & FromDayOfWeekToDaysOfWeek(currentDateTime.DayOfWeek)) == FromDayOfWeekToDaysOfWeek(currentDateTime.DayOfWeek)))
                                {
                                    n.IsDueForProcessing = true;
                                }
                                break;
                            case FrequencyType.Monthly:
                                if ((currentDateTime.Month - n.LastProcessedDate.Month) >= n.Frequency && n.DayOfMonth == currentDateTime.Day)
                                {
                                    n.IsDueForProcessing = true;
                                }
                                break;
                        }
                    }
                    else
                    {
                        n.IsDueForProcessing = true;
                    }

                    if (n.IsDueForProcessing)
                    {
                        Logger.Write("Notification # " + n.ID.ToString() + " has been flagged for processing");
                        strNotificationIDs+= n.ID.ToString() + ",";
                    }

                    
                }
                if (strNotificationIDs != "")
                {
                    Dictionary<int, List<int>> dicNotificationsUsers = new Dictionary<int, List<int>>();
                    dicNotificationsUsers = Notifications.GetNotificationsUsers(strNotificationIDs);
                    foreach (int userID in dicNotificationsUsers.Keys)
                    {
                        User userDetails = Tasks.GetUser(userID);
                        string[] notificationIDs = Array.ConvertAll<int, string>(dicNotificationsUsers[userID].ToArray(), new Converter<int, string>(Convert.ToString));
                        List<Task> lstUserTasks = (List<Task>)Tasks.GetTasksByNotification(userID, string.Join(",", notificationIDs) + ",");
                        Logger.Write(lstUserTasks.Count.ToString() + " Tasks have been found for the user " + userID.ToString());
                        Classes.Notification firstNotification = null;
                        if (dicNotificationsUsers[userID].Count>0) 
                        {
                            firstNotification = Notifications.GetNotification(dicNotificationsUsers[userID][0]);
                        }
                        List<NotificationContent> contents = (List<NotificationContent>)Notifications.GetUniqueNotificationContents(string.Join(",", notificationIDs) + ",");
                        foreach (NotificationContent content in contents) 
                        {
                            StringBuilder sbContent = new StringBuilder();
                            if (content.IsHtml)
                            {
                                sbContent.Append(System.IO.File.ReadAllText(ServiceConfiguration.ServiceExecutableDirectory + ServiceConfiguration.MailTemplate));
                            }
                            else
                            {
                                sbContent.Append("$NotificationContent$");
                            }
                            sbContent.Replace("$NotificationContent$", content.Body);


                            Logger.Write("Generating serialized task XML");
                            
                            StringBuilder  sbTasksXml = new StringBuilder();
                            StringWriter sw = new StringWriter(sbTasksXml);
                            XmlSerializer serializer = new XmlSerializer(typeof(List<Task>));
                            serializer.Serialize(sw, lstUserTasks);
                            serializer = null;
                            sw.Close();
                            sw=null;


                            Logger.Write("Replacing dynamic texts in content");

                            StringBuilder sbTranformedHtml = new StringBuilder();
                            XslCompiledTransform transform = new XslCompiledTransform();
                            if (sbContent.ToString().IndexOf("$TasksSummaryTable$") > 0)
                            {
                                Logger.Write("Transforming $TasksSummaryTable$");
                                transform.Load(ServiceConfiguration.ServiceExecutableDirectory + ServiceConfiguration.TasksSummaryTableXsl);
                                transform.Transform(new XmlTextReader(new StringReader(sbTasksXml.ToString())), null, new XmlTextWriter(new StringWriter(sbTranformedHtml)), null);
                                transform = null;
                                sbContent.Replace("$TasksSummaryTable$", sbTranformedHtml.ToString());
                            }

                            if (sbContent.ToString().IndexOf("$TaskOwner$") > 0)
                            {
                                Logger.Write("Transforming $TaskOwner$");
                                sbTranformedHtml = new StringBuilder();
                                transform = new XslCompiledTransform();
                                transform.Load(ServiceConfiguration.ServiceExecutableDirectory + ServiceConfiguration.TaskOwnerXsl);
                                transform.Transform(new XmlTextReader(new StringReader(sbTasksXml.ToString())), null, new XmlTextWriter(new StringWriter(sbTranformedHtml)), null);
                                transform = null;
                                sbContent.Replace("$TaskOwner$", sbTranformedHtml.ToString());
                            }

                            Logger.Write("Getting the notification processor");
                            INotificationProcessor notificationProcessor = GetNotificationProcessor();

                            if (notificationProcessor != null) 
                            {
                                EmailNotification nd = new EmailNotification();

                                //Currently only one notification recipient is supported.
                                if (firstNotification.Recipients[0].Address.Trim() == "$TaskOwner$") 
                                    nd.To.Add(userDetails.EmailAddress);
                                else
                                    nd.To.Add(firstNotification.Recipients[0].Address);

                                nd.Subject = firstNotification.Subject;
                                nd.Body = sbContent.ToString();

                                nd.IsBodyHtml = content.IsHtml;
                                nd.Priority = System.Net.Mail.MailPriority.Normal;

                                foreach (string s in nd.To)
                                {
                                    Logger.Write("Sending Notification to " + s);
                                }
                                
                                notificationProcessor.Send(nd);

                            }

                            sbContent = null;
                            sbTasksXml = null;
                            sbTranformedHtml = null;
                        }
                    }
                    Logger.Write("Updating database to indicate the notification has been processed");
                    List<int> notifications = new List<int>();
                    foreach (List<int> j in dicNotificationsUsers.Values)
                    {
                        foreach (int i in j)
                            if (!notifications.Contains(i))
                                notifications.Add(i);
                    }

                    foreach (int i in notifications)
                    {
                        Notifications.AddProcesssedNotification(i, currentDateTime);
                    }
                }

            }
        }
        
        /// <summary>
        /// Converts non flag based DayOfWeek enumeration to flag based DaysOfWeek
        /// </summary>
        /// <param name="weekday"></param>
        /// <returns></returns>
        private static DaysOfWeek FromDayOfWeekToDaysOfWeek(DayOfWeek weekday)
        {
            DaysOfWeek weekday1 = DaysOfWeek.None;
            if (weekday == DayOfWeek.Sunday)
                weekday1 = weekday1 | DaysOfWeek.Sunday;

            if (weekday == DayOfWeek.Monday)
                weekday1 = weekday1 | DaysOfWeek.Monday;

            if (weekday == DayOfWeek.Tuesday)
                weekday1 = weekday1 | DaysOfWeek.Tuesday;

            if (weekday == DayOfWeek.Wednesday)
                weekday1 = weekday1 | DaysOfWeek.Wednesday;

            if (weekday == DayOfWeek.Thursday)
                weekday1 = weekday1 | DaysOfWeek.Thursday;

            if (weekday == DayOfWeek.Friday)
                weekday1 = weekday1 | DaysOfWeek.Friday;

            if (weekday == DayOfWeek.Saturday)
                weekday1 = weekday1 | DaysOfWeek.Saturday;


            return weekday1;
        }

        private static INotificationProcessor GetNotificationProcessor() 
        {
            try 
            {
                return (INotificationProcessor)Activator.CreateInstance(ServiceConfiguration.NotificationType);
            }
            catch { throw; }
        }
    }
}
